﻿using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using LowLevelGraphics;

namespace LowLevelGraphics.Drawing.FloodFillModes
{
    /// <summary>
    /// 
    /// </summary>
    public class FloodFill3 : AbstractFloodFill
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FloodFill3"/> class.
        /// </summary>
        public FloodFill3()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FloodFill3"/> class.
        /// </summary>
        /// <param name="_floodFill3">The _flood fill3.</param>
        internal FloodFill3(FloodFill3 _floodFill3)
        {
        }

        /// <summary>
        /// Executes a flood fill.
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="color">The color.</param>
        protected void FloodFill(UnsafeBitmap bitmap, int x, int y, Color color)
        {
            BitmapData data = bitmap.BitmapData;
            int[] bits = new int[data.Stride / 4 * data.Height];
            Marshal.Copy(data.Scan0, bits, 0, bits.Length);

            LinkedList<Point> check = new LinkedList<Point>();
            int floodTo = color.ToArgb();
            int floodFrom = bits[x + y * data.Stride / 4];
            bits[x + y * data.Stride / 4] = floodTo;

            if (floodFrom != floodTo)
            {
                check.AddLast(new Point(x, y));
                while (check.Count > 0)
                {
                    Point cur = check.First.Value;
                    check.RemoveFirst();

                    foreach (Point off in new Point[] {
                new Point(0, -1), new Point(0, 1), 
                new Point(-1, 0), new Point(1, 0)})
                    {
                        Point next = new Point(cur.X + off.X, cur.Y + off.Y);
                        if (next.X >= 0 && next.Y >= 0 &&
                            next.X < data.Width &&
                            next.Y < data.Height)
                        {
                            if (bits[next.X + next.Y * data.Stride / 4] == floodFrom)
                            {
                                check.AddLast(next);
                                bits[next.X + next.Y * data.Stride / 4] = floodTo;
                            }
                        }
                    }
                }
            }

            Marshal.Copy(bits, 0, data.Scan0, bits.Length);
        }

        /// <summary>
        /// Fills the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="_nX">The _n X.</param>
        /// <param name="_nY">The _n Y.</param>
        /// <param name="_color">The _color.</param>
        public override void Fill(UnsafeBitmap _bitmap, int _nX, int _nY, Color _color)
        {
            FloodFill(_bitmap, _nX, _nY, _color);
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public override object Clone()
        {
            return new FloodFill3(this);
        }
    }
}
